Imports System.Threading
Imports System.Text
Imports System.Security.Cryptography
Imports System.IO.Compression
Imports System.Windows.Forms

Public Module Update
    Dim intVer As Integer
    Dim strURI As String
    Dim strStart As String
    Dim strEnd As String
    Dim bolForce As Boolean

    Public Sub CheckUpdate(BuildTime As String, UpdateInfoFile As String, Optional StartFlag As String = "", Optional EndFlag As String = "", Optional Isforce As Boolean = False, Optional IsThread As Boolean = True)
        intVer = ToTimestamp(Date.Parse(BuildTime))
        strURI = UpdateInfoFile
        strStart = StartFlag
        strEnd = EndFlag
        bolForce = Isforce

        If IsThread Then
            Dim CheckThread = New Thread(AddressOf CheckUpdateThread)
            CheckThread.Start()
        Else
            CheckUpdateThread()
        End If
    End Sub

    Private Sub CheckUpdateThread()
        Dim strData As String
        Dim strVerLog As String
        Dim strItem() As String
        Dim strLeft As String
        Dim strMd5 As String
        Dim strLog As String
        Dim Web As New Net.WebClient

        Try
            strData = Web.DownloadString(strURI + "?t=" + ToTimestamp(Now()).ToString)
        Catch ex As Exception
            Exit Sub
        End Try
        Try
            strVerLog = GetString(StrTrim(strData), strStart, strEnd)
            strLeft = strVerLog.Substring(0, strVerLog.Length - 33)
            strMd5 = strVerLog.Substring(strVerLog.Length - 32, 32)
            If GetStringMD5(strLeft) = strMd5.ToLower Then
                strItem = strVerLog.Split(",")
                strLog = Base64ToString(strItem(1))
                If CInt(strItem(0)) > intVer Then
                    Dim msg As DialogResult
                    msg = MessageBox.Show("发现新的软件版本，请点击" + Chr(34) + "确定" + Chr(34) +
                                          "按钮下载更新，也可以点击" + Chr(34) + "取消" + Chr(34) + "按钮稍后更新。" + vbCrLf + vbCrLf +
                                          "更新日志：" + vbCrLf + strLog, "提示",
                                          MessageBoxButtons.OKCancel, MessageBoxIcon.Information,
                                          MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification)
                    If msg = DialogResult.OK Then
                        Web.DownloadFile(Base64ToString(strItem(2)), "NewUpdate.zip")
                        If GetFileMD5(Application.StartupPath & "\NewUpdate.zip") = strItem(3).ToLower Then
                            ExtractZip(Application.StartupPath & "\NewUpdate.zip", Application.StartupPath, True)
                            IO.File.WriteAllBytes(Application.StartupPath & "\NewUpdate.bat", Convert.FromBase64String(strItem(4)))
                            Dim pInfo As New ProcessStartInfo With {
                                .FileName = "NewUpdate.bat",
                                .WorkingDirectory = Application.StartupPath
                            }
                            Process.Start(pInfo)
                            Application.Exit()
                        Else
                            Throw New Exception("更新包校验失败")
                        End If
                    Else
                        If bolForce = True Then Application.Exit()
                    End If
                End If
            Else
                Throw New Exception("更新配置文件校验失败")
            End If
        Catch ex As Exception
            MessageBox.Show("软件更新下载失败，请稍后继续重试。" + vbCrLf + vbCrLf + "错误信息：" + vbCrLf + ex.Message, "错误",
                            MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification)
        End Try
    End Sub

    Public Sub ExtractZip(ZipPath As String, ExtractPath As String, Overwrite As Boolean)
        ExtractPath = IO.Path.GetFullPath(ExtractPath)
        If Not ExtractPath.EndsWith(IO.Path.DirectorySeparatorChar) Then
            ExtractPath += IO.Path.DirectorySeparatorChar
        End If

        Using archive As ZipArchive = ZipFile.OpenRead(ZipPath)
            For Each entry As ZipArchiveEntry In archive.Entries
                Dim destinationPath As String = IO.Path.GetFullPath(IO.Path.Combine(ExtractPath, entry.FullName))
                If destinationPath.Substring(destinationPath.Length - 1, 1) = "/" Or
                    destinationPath.Substring(destinationPath.Length - 1, 1) = "\" Then
                    IO.Directory.CreateDirectory(destinationPath)
                Else
                    If (IO.File.Exists(destinationPath) And Overwrite) Or IO.File.Exists(destinationPath) = False Then
                        entry.ExtractToFile(destinationPath, True)
                    End If
                End If
            Next
        End Using
    End Sub

    Public Function GetFileMD5(strSource As String) As String
        Dim result As String = ""
        Try
            Dim fstream As New IO.FileStream(strSource, IO.FileMode.Open, IO.FileAccess.Read)
            Dim dataToHash(fstream.Length - 1) As Byte
            fstream.Read(dataToHash, 0, fstream.Length)
            fstream.Close()
            Dim hashvalue() As Byte = CType(CryptoConfig.CreateFromName("MD5"), HashAlgorithm).ComputeHash(dataToHash)
            Dim i As Integer
            For i = 0 To hashvalue.Length - 1
                result += Right("00" + Hex(hashvalue(i)).ToLower, 2)
            Next
            Return result
        Catch ex As Exception
            'MsgBox(ex.Message)
            Return result
        End Try
    End Function

    Public Function GetStringMD5(strString As String) As String
        Dim result As String = ""
        Try
            Dim hashvalue() As Byte = CType(CryptoConfig.CreateFromName("MD5"), HashAlgorithm).ComputeHash(Encoding.Default.GetBytes(strString))
            Dim i As Integer
            For i = 0 To hashvalue.Length - 1
                result += Right("00" + Hex(hashvalue(i)).ToLower, 2)
            Next
            Return result
        Catch ex As Exception
            'MsgBox(ex.Message)
            Return result
        End Try
    End Function

    Public Function StringToBase64(strData As String) As String
        Return Convert.ToBase64String(Encoding.UTF8.GetBytes(strData))
    End Function

    Public Function Base64ToString(strData As String) As String
        Return Encoding.UTF8.GetString(Convert.FromBase64String(strData))
    End Function

    Public Function ToTimestamp(Times As Date) As Long
        Return (Times.Ticks - TimeZone.CurrentTimeZone.ToLocalTime(New DateTime(1970, 1, 1, 0, 0, 0, 0)).Ticks) / 10000000
    End Function

    Public Function FromTimestamp(Timestamp As Long) As Date
        Dim tt As Long
        tt = TimeZone.CurrentTimeZone.ToLocalTime(New DateTime(1970, 1, 1, 0, 0, 0, 0)).Ticks + Timestamp * 10000000
        Return New DateTime(tt)
    End Function

    Public Function GetString(strData As String, strStart As String, strEnd As String) As String
        Dim intStart As Integer
        Dim intEnd As Integer
        If strStart = "" Then
            intStart = 0
        Else
            intStart = strData.IndexOf(strStart) + strStart.Length
        End If
        If strData.IndexOf(strStart) = -1 Or strData.IndexOf(strEnd, intStart) = -1 Then
            If strStart <> "" And strEnd <> "" Then Return ""
        End If
        If strEnd = "" Then
            Return strData.Substring(intStart)
        Else
            intEnd = strData.IndexOf(strEnd, intStart) - strEnd.Length
            Return strData.Substring(intStart, intEnd)
        End If
    End Function

    Public Function StrTrim(strData As String) As String
        Dim strTmp As String
        strTmp = strData.Replace(vbCr, "")
        strTmp = strTmp.Replace(vbLf, "")
        strTmp = strTmp.Replace(" ", "")
        Return strTmp
    End Function
End Module
